﻿using Castle.DynamicProxy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DynamicAgent
{
    /// <summary>
    /// AOP
    /// </summary>
    public class AopCommonAsyncInterceptor : AsyncInterceptorBase
    {
        private readonly IEnumerable<IAopInterceptor> aopInterceptors;

        public AopCommonAsyncInterceptor(IEnumerable<IAopInterceptor> aopInterceptors)
        {
            this.aopInterceptors = aopInterceptors;
        }

        protected override async Task InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func<IInvocation, IInvocationProceedInfo, Task> proceed)
        {
            Before(invocation);
            await BeforeAsync(invocation);
            try
            {
                await proceed(invocation, proceedInfo);
                await AfterAsync(invocation);
                After(invocation);
            }
            catch (Exception ex)
            {
                await AfterAsync(invocation, ex);
                After(invocation, ex);
                throw;
            }
        }

        protected override async Task<TResult> InterceptAsync<TResult>(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func<IInvocation, IInvocationProceedInfo, Task<TResult>> proceed)
        {
            Before(invocation);
            await BeforeAsync(invocation);
            try
            {
                var ret = await proceed(invocation, proceedInfo);
                await AfterAsync(invocation);
                After(invocation);
                return ret;
            }
            catch (Exception ex)
            {
                await AfterAsync(invocation, ex);
                After(invocation, ex);
                throw;
            }
        }

        private void Before(IInvocation invocation)
        {
            foreach (var item in aopInterceptors)
            {
                item.Before(invocation.Method, invocation.MethodInvocationTarget);
            }
        }
        private void After(IInvocation invocation, Exception? ex = default)
        {
            foreach (var item in aopInterceptors)
            {
                item.After(invocation.Method, invocation.MethodInvocationTarget, ex);
            }
        }
        private async ValueTask BeforeAsync(IInvocation invocation)
        {
            foreach (var item in aopInterceptors)
            {
                await item.BeforeAsync(invocation.Method, invocation.MethodInvocationTarget);
            }

        }

        private async ValueTask AfterAsync(IInvocation invocation, Exception? ex = default)
        {
            foreach (var item in aopInterceptors)
            {
                await item.AfterAsync(invocation.Method, invocation.MethodInvocationTarget, ex);
            }
        }
    }
}
